﻿using System;
using System.Collections;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Xml.Serialization;

namespace CompanyX.Shared.Utilities
{
    ///////////////////////////////////////////////////////////////////////////////////////////////
    ///// <summary>
    ///// Architecture service for serialization
    ///// </summary>
    ///////////////////////////////////////////////////////////////////////////////////////////////
    public sealed class SerializationManager
    {
        private SerializationManager() { }

        // <summary>
        // Serializes an object to either an xml string or a Base64 encoded string.
        // </summary>
        // <param name="objX">object to be serialized.</param>
        // <param name="useBinary">Flag to indicate whether object should be serialized to xml or binary.
        // if ( true, then will serialize to binary, else, will serialize to xml.</param>
        // <returns>Serialized object</returns>
        public static string Serialize(object objX, bool useBinary)
        {
            string retVal = "";

            if (useBinary)
            {
                // First serialize the object into byte array
                byte[] btarr = SerializeToByteArray(objX);

                //Base64 encode byte array
                retVal = Convert.ToBase64String(btarr);
            }
            else
            {
                //Create xmlSerializer variable
                XmlSerializer xmls = new XmlSerializer(objX.GetType());

                //Create a memory stream
                MemoryStream stream = new MemoryStream();

                xmls.Serialize(stream, objX);

                //return out stream contents
                retVal = GetStringFromStream(stream);
            }

            return retVal;
        }

        /// <summary>
        /// Use this Serialize method if qualified namespace descripters are required in the result xml
        /// </summary>
        /// <param name="objX"></param>
        /// <param name="ns"></param>
        /// <returns></returns>
        public static string Serialize(object objX, XmlSerializerNamespaces ns)
        {
            string retVal = "";

            //Create xmlSerializer variable
            XmlSerializer xmls = new XmlSerializer(objX.GetType());

            //Create a memory stream
            MemoryStream stream = new MemoryStream();

            xmls.Serialize(stream, objX, ns);

            //return out stream contents
            retVal = GetStringFromStream(stream);

            return retVal;
        }

        /// <summary>
        /// Use this Serialize method if qualified namespace descripters are required in the result xml
        /// </summary>
        /// <param name="objX"></param>
        /// <param name="defaultNamespace"></param>
        /// <returns></returns>
        public static string Serialize(object objX, string defaultNamespace)
        {
            string retVal = "";

            XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
            ns.Add("zz0", defaultNamespace);

            //Create xmlSerializer variable
            XmlSerializer xmls = new XmlSerializer(objX.GetType());

            //Create a memory stream
            MemoryStream stream = new MemoryStream();

            xmls.Serialize(stream, objX, ns);

            //return out stream contents
            retVal = GetStringFromStream(stream);

            return retVal;
        }

        // <summary>
        // Binary Serializes an object to a byte array
        // </summary>
        // <param name="objX">object to be serialized.</param>
        // <returns>byte Array</returns>
        public static byte[] SerializeToByteArray(object objX)
        {
            // First serialize the object into a memory stream
            MemoryStream stream = new MemoryStream();
            BinaryFormatter objBinaryFormat = new BinaryFormatter();
            objBinaryFormat.Serialize(stream, objX);
            objBinaryFormat = null;

            // Convert the stream into a byte array
            stream.Position = 0;
            return stream.ToArray();
        }

        // <summary>
        // Binary Serializes and compresses an object to a byte array
        // </summary>
        // <param name="objX">object to be serialized.</param>
        // <returns>byte Array</returns>
        public static byte[] SerializeAndCompressToByteArray(object objX)
        {
            // First serialize the object into a memory stream
            MemoryStream stream = new MemoryStream();
            BinaryFormatter objBinaryFormat = new BinaryFormatter();
            objBinaryFormat.Serialize(stream, objX);
            objBinaryFormat = null;

            // Convert the stream into a byte array
            stream.Position = 0;
            byte[] byteArray = stream.ToArray();

            System.IO.MemoryStream ms = new System.IO.MemoryStream();
            System.IO.Compression.GZipStream sw = new System.IO.Compression.GZipStream(ms,
                System.IO.Compression.CompressionMode.Compress);

            //Compress
            sw.Write(byteArray, 0, byteArray.Length);
            //Close, DO NOT FLUSH cause bytes will go missing...
            sw.Close();

            //Transform byte[] zip data to string
            byteArray = ms.ToArray();
            return byteArray;
        }

        /// <summary>
        /// Decompress and deserialize a byte array
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="btarr"></param>
        /// <param name="defaultNamespace"></param>
        /// <param name="maxDecompressedSize">Number of bytes to allocate for the buffer to contain the decompressed data</param>
        /// <returns></returns>
        public static T DecompressDeserializeByteArray<T>(byte[] btarr, string defaultNamespace, int maxDecompressedSize)
        {
            if (maxDecompressedSize < 1)
            {
                maxDecompressedSize = 32767;
            }
            MemoryStream memoryStream = null;
            byte[] byteArray = new byte[maxDecompressedSize];

            try
            {
                //Prepare for decompress
                System.IO.MemoryStream ms = new System.IO.MemoryStream(btarr);
                System.IO.Compression.GZipStream sr = new System.IO.Compression.GZipStream(ms,
                    System.IO.Compression.CompressionMode.Decompress);

                //Decompress
                int rByte = sr.Read(byteArray, 0, byteArray.Length);

                // Convert the decompressed bytes into a stream
                memoryStream = new MemoryStream(byteArray, 0, rByte);

                // deserialize the stream into an object graph
                BinaryFormatter BinaryFormatter = new BinaryFormatter();
                return (T)BinaryFormatter.Deserialize(memoryStream);
            }
            finally
            {
                if (memoryStream != null)
                {
                    memoryStream.Close();
                    memoryStream.Dispose();
                }
            }
        }

        // <summary>
        // Deserializes a Stream object.
        // </summary>
        // <param name="stream">Stream to be deserialized.</param>
        // <param name="stream">Stream to be deserialized.</param>
        // <returns>Deserialized object in the generic type.</returns>
        public static T Deserialize<T>(Stream stream, string defaultNamespace)
        {
            //Create xmlSerializer variable
            XmlSerializer xmls = null;
            if (!string.IsNullOrEmpty(defaultNamespace))
                xmls = new XmlSerializer(typeof(T), defaultNamespace);
            else
                xmls = new XmlSerializer(typeof(T));

            stream.Position = 0;
            return (T)xmls.Deserialize(stream);
        }


        // <summary>
        // Deserializes either an xml serialized or base64 binary serialized object.
        // </summary>
        // <param name="strObject">Either an xml serialized or binary serialized object.</param>
        // <param name="binaryString">Flag to determine form of deserialization. 
        // if ( strObject is a binary string, then this value should be true. 
        // if ( strObject is an xml string, then this value should be false.</param>
        // <returns>Deserialized object in the generic type</returns>
        public static T Deserialize<T>(string strObject, bool binaryString, string defaultNamespace)
        {
            if (binaryString)
            {
                MemoryStream memoryStream = null;

                try
                {
                    // convert the string into a stream
                    byte[] btarr = Convert.FromBase64String(strObject);
                    memoryStream = new MemoryStream(btarr);

                    // deserialize the stream into an object graph
                    BinaryFormatter BinaryFormatter = new BinaryFormatter();
                    return (T)BinaryFormatter.Deserialize(memoryStream);
                }
                finally
                {
                    if (memoryStream != null)
                    {
                        memoryStream.Close();
                        memoryStream.Dispose();
                    }
                }
            }
            else
            {
                StringReader stringReader = null;

                try
                {
                    //Create xmlSerializer variable
                    XmlSerializer xmls = null;
                    if (!string.IsNullOrEmpty(defaultNamespace))
                        xmls = new XmlSerializer(typeof(T), defaultNamespace);
                    else
                        xmls = new XmlSerializer(typeof(T));

                    //Create string  Reader
                    stringReader = new StringReader(strObject);

                    //Deserialize object
                    return (T)xmls.Deserialize(stringReader);
                }
                finally
                {
                    if (stringReader != null)
                    {
                        stringReader.Close();
                        stringReader.Dispose();
                    }
                }
            }
        }


        // <summary>
        // Deserializes either an xml serialized or binary serialized object.
        // </summary>
        // <param name="strObject">Either an xml serialized or binary serialized object.</param>
        // <param name="binaryString">Flag to determine form of deserialization. 
        // if ( strObject is a binary string, then this value should be true. 
        // if ( strObject is an xml string, then this value should be false.</param>
        // <returns>Deserialized object in the generic type</returns>
        public static T DeserializeByteArray<T>(byte[] btarr, string defaultNamespace)
        {
            MemoryStream memoryStream = null;

            try
            {
                // convert the bytes into a stream
                memoryStream = new MemoryStream(btarr);

                // deserialize the stream into an object graph
                BinaryFormatter BinaryFormatter = new BinaryFormatter();
                return (T)BinaryFormatter.Deserialize(memoryStream);
            }
            finally
            {
                if (memoryStream != null)
                {
                    memoryStream.Close();
                    memoryStream.Dispose();
                }
            }
        }

        private static string GetStringFromStream(MemoryStream strm1)
        {
            strm1.Position = 0;
            byte[] btarr = strm1.ToArray();
            Encoding Encoder = Encoding.UTF8;
            string retVal = Encoder.GetString(btarr);
            return retVal;
        }
    }
}